package org.concepts.java.classloader.hello;

import java.io.File;

import org.concepts.java.classloader.utils.ClassLoaderUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class TestFileClassLoader {

  private FileClassLoader fileCL = null;

  private static final String IndependentClassName = "Independent";

  private static final String ADependsOnBClassName = "ADependsOnB";

  private static final String BClassName = "B";

  /**
   * FIXME: Fix hard coded path
   */
  private final String testInputClasses = "/home/vvvv/workspace/java-classloader/common/test-input/classes";

  @Before
  public void setUp() {
    this.fileCL = new FileClassLoader(new File(this.testInputClasses));
  }

  /**
   * Ensures that we are able to load a class using {@link FileClassLoader} and
   * then instantiate it.
   * 
   * @throws ClassNotFoundException
   * @throws InstantiationException
   * @throws IllegalAccessException
   */
  @Test
  public void testLoading() throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {
    System.out.println("\nFile classloader [" + fileCL + "]");
    ClassLoaderUtils.printParentCLs(fileCL);
    Class<?> independentClass = fileCL.loadClass(IndependentClassName);
    System.out.println(independentClass + "class loaded by classloader ["
        + independentClass.getClassLoader() + "]");

    Assert.assertEquals(1, this.fileCL.loadCount(IndependentClassName));

    // Ensure that we are able to create the class loaded by the classloader
    independentClass.newInstance();

  }

  /**
   * Test that {@Link FileClassLoader} is only requested to load the
   * class the first time.
   * 
   * @throws ClassNotFoundException
   */
  @Test
  public void testMultipleLoading() throws ClassNotFoundException {
    fileCL.loadClass(IndependentClassName);
    Assert.assertEquals(1, this.fileCL.loadCount(IndependentClassName));
    fileCL.loadClass(IndependentClassName);
    Assert.assertEquals(1, this.fileCL.loadCount(IndependentClassName));
  }

  /**
   * Test that {@Link FileClassLoader} does not need to bother about
   * figuring out and loading dependency.
   * 
   * @throws ClassNotFoundException
   * @throws IllegalAccessException
   * @throws InstantiationException
   */
  @Test
  public void testLoadingDependency() throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {

    Class<?> cls = fileCL.loadClass(ADependsOnBClassName);
    Assert.assertEquals(1, this.fileCL.loadCount(ADependsOnBClassName));

    // B is not loaded initially, as the JVM was only requested to load the
    // bit. The object was never instantiated.
    // NOTE: This behavior can change from JVM to JVM. It's upto the VM to
    // decide when it's a good time to load dependent classes.
    Assert.assertEquals(0, this.fileCL.loadCount(BClassName));

    cls.newInstance();
    // During ADependsOnB object creation the JVM figures out that it needs
    // to load B and asks FileClassLoader to do so.
    Assert.assertEquals(1, this.fileCL.loadCount(BClassName));

  }

  @Test
  public void testClassLoaderInteraction() throws ClassNotFoundException,
      InstantiationException, IllegalAccessException {
    Class<?> cls1 = fileCL.loadClass(IndependentClassName);
    Object obj1 = cls1.newInstance();

    FileClassLoader fileCL2 = new FileClassLoader(new File(
        this.testInputClasses));
    Class<?> cls2 = fileCL2.loadClass(IndependentClassName);
    Object obj2 = cls2.newInstance();

  }
}
